/* Copyright (C) 2010 Luca Piccinelli
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "common/utility_data_structures.h"
#include "common/pdl.h"

#include <boost/algorithm/string.hpp>

#include <sstream>
#include <stdexcept>

namespace NAMESPACE{

// ****** Key implementation ***************************************************
const std::string Key::KEY_SEPARATOR = ".";

Key::~Key(){}
Key::Key(){}
Key::Key(const Key& k) : ns(k.ns), key_name(k.key_name){}
Key& Key::operator=(const Key& k){
    if(&k == this) return *this;

    ns = k.ns; key_name = k.key_name;

    return *this;
}

std::string Key::get_key() const{
    return ns + KEY_SEPARATOR + key_name;
}

void Key::set_key(const std::string& key_str){
    std::vector<std::string> key_tokens;
    boost::split(key_tokens, key_str, boost::is_any_of(Key::KEY_SEPARATOR));
    
    ns = key_tokens[0];
    std::stringstream ss;
    ss << key_tokens[1];
    for(int i = 2; i < key_tokens.size(); i++) ss << Key::KEY_SEPARATOR << key_tokens[i];
    key_name = ss.str();
}
                
void Key::set_key(const std::string& _ns, const std::string& _key_name){
    ns = _ns; key_name = _key_name;
}

bool Key::operator==(const Key& k) const{
    return ns == k.ns && key_name == k.key_name;
}

bool Key::operator!=(const Key& k) const{
    return !Key::operator==(k);
}

bool Key::operator<(const Key& k) const{
    return get_key() < k.get_key();
}
// -----------------------------------------------------------------------------

// ****** IOKey implementation *************************************************
IOKey::~IOKey(){ delete pdlElement; }
IOKey::IOKey() : Key(), type(), pdlElement(0){}
IOKey::IOKey(const IOKey& k) : Key(k), 
                               type(k.type), 
                               pdlElement(k.pdlElement ? k.pdlElement->cloneSerializable() : 0){}
IOKey& IOKey::operator=(const IOKey& k){
    if(&k == this) return *this;

    SerializablePDLElement* tmp = pdlElement;
    try{
        if(k.pdlElement) pdlElement = k.pdlElement->cloneSerializable();
        else pdlElement = 0;
    }catch(...){
        pdlElement = tmp;
        throw;
    }
    if(tmp) delete tmp;
    
    Key::operator=(k);
    type = k.type;

    return *this;
}

std::string IOKey::getType() const{
    return type;
}

void IOKey::set_key_and_type(const std::string& key_type_str){
    std::vector<std::string> key_tokens;
    boost::split(key_tokens, key_type_str, boost::is_any_of(Key::KEY_SEPARATOR));
    
    ns = key_tokens[0];
    std::stringstream ss;
    ss << key_tokens[1];
    for(int i = 2; i < key_tokens.size() - 1; i++) ss << Key::KEY_SEPARATOR << key_tokens[i];
    key_name = ss.str();
    type = key_tokens.back();
}

IOKey& IOKey::setType(const std::string& _type){
    type = _type;
    return *this;
}

IOKey& IOKey::setAcceptedValues(const std::string& _accepted){
    throw std::runtime_error("Not implemented");
    return *this;
}

IOKey& IOKey::setAcceptedValues(const SerializablePDLElement& accepted){
    SerializablePDLElement* tmp = pdlElement;
    try{
        pdlElement = accepted.cloneSerializable();
    }catch(...){
        pdlElement = tmp;
        throw;
    }
    
    if(tmp) delete tmp;
    return *this;
}

const SerializablePDLElement& IOKey::getAcceptedValues() throw(gestface_exception){
    if(pdlElement)
        return *pdlElement;
    else
        throw gestface_exception("No accepted values set");
}

bool IOKey::operator==(const Key& k) const{
    if(typeid(k) != typeid(IOKey)) return false;
    if(!Key::operator==(k)) return false;
    const IOKey* tmpk = dynamic_cast<const IOKey*>(&k);
    return tmpk->type == type;
}

bool IOKey::operator!=(const Key& k) const{
    return !IOKey::operator==(k);
}

bool IOKey::operator<(const Key& k) const{
    const IOKey tmpk = dynamic_cast<const IOKey&>(k);
    return get_key() + type < tmpk.get_key() + tmpk.type;
}
// -----------------------------------------------------------------------------

// ****** related functions ****************************************************
std::ostream& operator<<(std::ostream os, const Key& k){
    return os << k.get_key();
}
// -----------------------------------------------------------------------------
}
